跳到主要内容

Go 模拟一个惰性生成器

编写一个惰性生成器

这里先尝试编写一个简单的惰性生成器

import (
"fmt"
)

var resume chan int

func integers() chan int {
yield := make(chan int)
count := 0
go func() {
for {
yield <- count
count++
}
}()
return yield
}

func generateInteger() int {
return <-resume
}

func main() {
resume = integers()
fmt.Println(generateInteger()) //=> 0
fmt.Println(generateInteger()) //=> 1
fmt.Println(generateInteger()) //=> 2
}

编写一个通用的惰性生产器

import (
"fmt"
)

type Any interface{}
type EvalFunc func(Any) (Any, Any)

func main() {
evenFunc := func(state Any) (Any, Any) {
os := state.(int)
ns := os + 2
return os, ns
}

even := BuildLazyIntEvaluator(evenFunc, 0)

// 模拟调用 10 次
for i := 0; i < 10; i++ {
fmt.Printf("%vth even: %v\n", i, even())
}
}

// BuildLazyEvaluator 通用的惰性生产器的工厂函数
func BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {
retValChan := make(chan Any)
loopFunc := func() {
var actState Any = initState
var retVal Any
for {
retVal, actState = evalFunc(actState)
retValChan <- retVal
}
}

retFunc := func() Any {
return <-retValChan
}

go loopFunc()
return retFunc
}

func BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {
ef := BuildLazyEvaluator(evalFunc, initState)
return func() int {
return ef().(int)
}
}

输出:

0th even: 0
1th even: 2
2th even: 4
3th even: 6
4th even: 8
5th even: 10
6th even: 12
7th even: 14
8th even: 16
9th even: 18

模拟一个 yield

TODO: 待学习...

Python、ES6 等语言都有这个生成器 generator 和 yield,所以它们到底有什么用?

其实回顾学习 Unity 时就可以看到 Unity 通过这个生成器来实现异步操作,实际它就是在一个函数里面保存状态,每次调用只执行一部分

例如

function* Gen() {
let curr = 0
while (true) {
yield curr++;
}
}

每次调用这个 Gen() 只会得到当前的值

或者像 Unity 的协程那样使用,把一件事分开多步执行

function* Gen() {
// do something....
yield return

// do something....
yield return

// do something....
yield return
}

补充:实际上,这个 Gen() 就是生成了一个迭代器,包括 php 在内的大多数语言实现迭代器的方式,是通过一个可中断的函数完成的。而协程的一个特点就是执行中断,切换上下文。所以就有了通过迭代器函数去实现协程的方案。这种方案充分利用了迭代器可中断的特点来模拟协程中断,而利用闭包函数的上下文独立性,实现协程的上下文切换。

References

惰性生成器的实现